﻿using Common;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace MvcSmartTool.Common {
    /// <summary>
    /// Defines a view model's property that will be generated.
    /// </summary>
    public class PropertyGenerationModel {
        public bool IsDisplayName { get; set; }
        public bool IsVirtual { get; set; }

        /// <summary>
        /// Gets the full name of the property type.
        /// </summary>
        public string TypeName { get; private set; }

        /// <summary>
        /// Gets the name of the property.
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Gets a value indicating whether this instance is a list.
        /// </summary>
        public bool IsList { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether this property has a setter.
        /// </summary>
        public bool CanSet { get; private set; }

        /// <summary>
        /// Gets or sets a value indicating whether this property has a getter.
        /// </summary>
        public bool CanGet { get; private set; }

        /// <summary>
        /// Gets or sets the type of the element returned by the property.
        /// </summary>
        public Type ElementType { get; private set; }

        public string HtmlInputType { get; set; }
        public List<IGenerationAttribute> Attributes { get; private set; }

        public bool IsNullable { get; set; }
        /// <summary>
        /// Initializes a new instance of the <see cref="PropertyGenerationModel"/> class.
        /// </summary>
        /// <param name="parent">The type model that contains the property.</param>
        /// <param name="property">The property info to generate from.</param>
        public PropertyGenerationModel(TypeGenerationModel parent, PropertyInfo property) {
            // Let the property of the view model as the property of the model.
            Name = property.Name;
            IsVirtual = property.GetAccessors()[0].IsVirtual;
            // TODO: add Attributes

            var getter = property.GetGetMethod();
            CanGet = getter != null && getter.IsPublic;

            var setter = property.GetSetMethod();
            CanSet = setter != null && setter.IsPublic;
            IsNullable = property.PropertyType.IsGenericType && property.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>);
            // In this small demo, only support Lists to contain child view models.
            IsList = property.PropertyType.IsGenericType && 
                (property.PropertyType.GetGenericTypeDefinition() == typeof(List<>) || property.PropertyType.GetGenericTypeDefinition() == typeof(IList<>));

            // If this is a list, we'll need type of the elements,
            // not of the property itseld.
            var itemType = (IsList || IsNullable)
                        ? property.PropertyType.GetGenericArguments().First()
                        : property.PropertyType;


            ElementType = (IsList || IsNullable) ? itemType : property.PropertyType;
            
            // Get the type name in a format that can be placed into code
            // (i.e. MyType<T> rather than MyType`1 for generic types)
            TypeName = GetTypeName( IsList|| IsNullable? itemType : property.PropertyType);
            
            var attrs = property.GetCustomAttributes(false);
            Attributes = new List<IGenerationAttribute>();
            foreach (var attr in attrs) {
                if(attr is MaxLengthAttribute){
                    var tmp = attr as MaxLengthAttribute;
                    Attributes.Add(new MaxAttr(tmp.Length));
                } else if (attr is MinLengthAttribute) {
                    var tmp = attr as MinLengthAttribute;
                    Attributes.Add(new MinAttr(tmp.Length));
                } else if (attr is DataTypeAttribute) {
                    var tmp = attr as DataTypeAttribute;
                    Attributes.Add(new DataTypeAttr(tmp.DataType.ToString()));
                } else if (attr is RequiredAttribute) {
                    Attributes.Add(new RequiredAttr());
                } else if (attr is NameAttribute) {
                    IsDisplayName = true;
                }
            }
            HtmlInputType = GetHtmlInputType(IsList || IsNullable ? itemType : property.PropertyType);
        }
        private string GetTypeName(Type type) {
            if (type == typeof(Int32) || type == typeof(int)) {
                return "int";
            }
            if (type == typeof(Int64) || type == typeof(long)) {
                return "long";
            }
            if (type == typeof(string) || type == typeof(String)) {
                return "string";
            }
            // TODO: Implement more if you want
            return type.Name;
        }

        private string GetHtmlInputType(Type type) {
            /* color, date, datetime, datetime-local, email ,month, number, range, search, tel, time, url, week */
            if (type == typeof(Int32) || type == typeof(int) || type == typeof(Int64) || type == typeof(long) || type == typeof(float)
                || type == typeof(decimal) || type == typeof(double) || type == typeof(short)) {
                return "number";
            }
            if (type == typeof(DateTime)) {
                return "datetime";
            }
            var att = Attributes.FirstOrDefault(x => x is DataTypeAttr) as DataTypeAttr;
            if (att != null) {
                return att.Type.ToLower(); 
            }
            return "text";
        }
    }
}
